#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>

typedef struct
{
	int lfd_rtsp_socket;		//监听套接字
	int lfd_rtp_socket;			//rtp套接字
	int lfd_rtcp_socket;		//rtcp套接字
	int cfd_client;				//客户端通信套接字
	char client_ip[32];			//客户端ip
	int client_port;			//客户端端口
	
}RTSP_SERVER_T;

/*定义rtsp服务器全局变量*/
RTSP_SERVER_T g_rtsp_server;

#define SER_PORT  (8554)
#define SERVER_RTP_PORT  55532
#define SERVER_RTCP_PORT 55533
#define BUF_MAX_SIZE  (1024*10)

RTSP_SERVER_T *app_get_rtsp_server_param(void)
{
	return &g_rtsp_server;
}

/*创建tcp套接字，用于rtsp连接*/
static int  app_create_rtsp_socket(void)
{
	RTSP_SERVER_T *ptRtspServer = NULL;
	int opt = 1;
	int iRet = 0;
	struct sockaddr_in sev_addr;
	
	ptRtspServer = app_get_rtsp_server_param();
	if(NULL == ptRtspServer)
	{
		printf("get param failed\n");
		return -1;
	}

	ptRtspServer->lfd_rtsp_socket = socket(AF_INET,SOCK_STREAM,0);
	if(ptRtspServer->lfd_rtsp_socket < 0)
	{
		printf("socket failed\n");
		return -1;
	}

	sev_addr.sin_family = AF_INET;
	sev_addr.sin_port 	= htons(SER_PORT);
	sev_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	if(setsockopt(ptRtspServer->lfd_rtsp_socket, SOL_SOCKET,SO_REUSEADDR,(const int*)&opt,sizeof(opt)) < 0)
	{
		printf("setsockopt failed\n");
		return -1;
	}
	return 0;
}

/*创建udp套接字，用于rtp和rtcp连接*/
static int app_create_rtp_socket(void)
{
	RTSP_SERVER_T *ptRtspServer = NULL;
	int opt = 1;
	
	ptRtspServer = app_get_rtsp_server_param();
	if(NULL == ptRtspServer)
	{
		printf("get param failed\n");
		return -1;
	}

	ptRtspServer->lfd_rtp_socket = socket(AF_INET,SOCK_DGRAM,0);
	if(ptRtspServer->lfd_rtp_socket < 0)
	{
		printf("socket failed\n");
		return -1;
	}

	if(setsockopt(ptRtspServer->lfd_rtp_socket, SOL_SOCKET,SO_REUSEADDR,(const int*)&opt,sizeof(opt)) < 0)
	{
		printf("setsockopt failed\n");
		return -1;
	}
	
	return 0;
}

static int app_create_rtcp_socket(void)
{
	RTSP_SERVER_T *ptRtspServer = NULL;
	int opt = 1;
	
	ptRtspServer = app_get_rtsp_server_param();
	if(NULL == ptRtspServer)
	{
		printf("get param failed\n");
		return -1;
	}

	ptRtspServer->lfd_rtcp_socket = socket(AF_INET,SOCK_DGRAM,0);
	if(ptRtspServer->lfd_rtcp_socket < 0)
	{
		printf("socket failed\n");
		return -1;
	}

	if(setsockopt(ptRtspServer->lfd_rtcp_socket, SOL_SOCKET,SO_REUSEADDR,(const int*)&opt,sizeof(opt)) < 0)
	{
		printf("setsockopt failed\n");
		return -1;
	}
	
	return 0;
}

static int app_bind_sockaddr(void)
{
	RTSP_SERVER_T *ptRtspServer = NULL;
	int opt = 1;
	int iRet = 0;
	struct sockaddr_in sev_addr;
	
	ptRtspServer = app_get_rtsp_server_param();
	if(NULL == ptRtspServer)
	{
		printf("get param failed\n");
		return -1;
	}
	bzero(&sev_addr, 0);
	sev_addr.sin_family = AF_INET;
	sev_addr.sin_port 	= htons(SER_PORT);
	sev_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	printf("ptRtspServer->lfd_rtsp_socket:%d\n",ptRtspServer->lfd_rtsp_socket);
	
	iRet = bind(ptRtspServer->lfd_rtsp_socket, (struct sockaddr *)&sev_addr, sizeof(sev_addr));
	if(iRet < 0)
	{
		printf("bind error\n");
		return -1;
	}
	return 0;
}

static int app_accept_client(void)
{
	RTSP_SERVER_T *ptRtspServer = NULL;
	struct sockaddr_in cli_addr;
	socklen_t len = 0;

	memset(&cli_addr, 0, sizeof(cli_addr));
	len = sizeof(cli_addr);

	ptRtspServer = app_get_rtsp_server_param();
	if(NULL == ptRtspServer)
	{
		printf("get param failed\n");
		return -1;
	}

	ptRtspServer->cfd_client = accept(ptRtspServer->lfd_rtsp_socket, (struct sockaddr *)&cli_addr,&len);
	if(ptRtspServer < 0)
	{
		printf("accept failed\n");
		return -1;
	}
	
	snprintf(ptRtspServer->client_ip, sizeof(ptRtspServer->client_ip),"%s",inet_ntoa(cli_addr.sin_addr));

	ptRtspServer->client_port = ntohs(cli_addr.sin_port);

	//printf("ip:%s\n",ptRtspServer->client_ip);
	//printf("port:%d\n",ptRtspServer->client_port);
	return 0;
}

static char *getLineFromBuf(char *buf, char *line)
{
	while(*buf != '\n')
	{
		*line = *buf;
		line++;
		buf++;
	}
	*line = '\n';
	++line;
	*line = '\0';

	++buf;
	return buf;
}

static int handleCmd_OPTIONS(char *result, int cseq)
{
	sprintf(result,"RTSP/1.0 200 OK\r\n"
					"CSeq:%d\r\n"
					"Public:OPTIONS,DESCRIBE,SETUP,PLAY\r\n"
					"\r\n",
					cseq);
	return 0;
}

static int handleCmd_DESCRIBE(char *result,int cseq, char *url)
{
	char sdp[512];
	char localIP[128];

	sscanf(url, "rtsp://%[^:]:",localIP);

	sprintf(sdp, "v=0\r\n"
				 "o=- 9%ld 1 IN IP4 %s\r\n"
                 "t=0 0\r\n"
                 "a=control:*\r\n"
                 "m=video 0 RTP/AVP 96\r\n"
                 "a=rtpmap:96 H264/90000\r\n"
                 "a=control:track0\r\n",
                 time(NULL), localIP);
                 
    sprintf(result, "RTSP/1.0 200 OK\r\nCSeq: %d\r\n"
                    "Content-Base: %s\r\n"
                    "Content-type: application/sdp\r\n"
                    "Content-length: %d\r\n\r\n"
                    "%s",
                    (int)cseq,
                    url,
                    (int)strlen(sdp),
                    sdp);
    return 0;
}


static int handleCmd_SETUP(char* result, int cseq, int clientRtpPort)
{
    sprintf(result, "RTSP/1.0 200 OK\r\n"
                    "CSeq: %d\r\n"
                    "Transport: RTP/AVP;unicast;client_port=%d-%d;server_port=%d-%d\r\n"
                    "Session: 66334873\r\n"
                    "\r\n",
                    cseq,
                    clientRtpPort,
                    clientRtpPort+1,
                    SERVER_RTP_PORT,
                    SERVER_RTCP_PORT);
    
    return 0;
}

static int handleCmd_PLAY(char* result, int cseq)
{
    sprintf(result, "RTSP/1.0 200 OK\r\n"
                    "CSeq: %d\r\n"
                    "Range: npt=0.000-\r\n"
                    "Session: 66334873; timeout=60\r\n\r\n",
                    cseq);
    
    return 0;
}

static void doClient(int clientSockfd, const char* clientIP, int clientPort,
                        int serverRtpSockfd, int serverRtcpSockfd)
{
    char method[40];
    char url[100];
    char version[40];
    int cseq;
    int clientRtpPort, clientRtcpPort;
    char *bufPtr;
    char* rBuf = malloc(BUF_MAX_SIZE);
    char* sBuf = malloc(BUF_MAX_SIZE);
    char line[400];

    while(1)
    {
        int recvLen;

        recvLen = recv(clientSockfd, rBuf, BUF_MAX_SIZE, 0);
        if(recvLen <= 0)
            goto out;

        rBuf[recvLen] = '\0';
        printf("---------------C->S--------------\n");
        printf("%s", rBuf);

        /* 解析方法 */
        bufPtr = getLineFromBuf(rBuf, line);
        if(sscanf(line, "%s %s %s\r\n", method, url, version) != 3)
        {
            printf("parse err\n");
            goto out;
        }
        printf("method:%s,url:%s,version:%s\n",method,url,version);

        /* 解析序列号 */
        bufPtr = getLineFromBuf(bufPtr, line);
        if(sscanf(line, "CSeq: %d\r\n", &cseq) != 1)
        {
            printf("parse err\n");
            goto out;
        }
        printf("cseq:%d\n",cseq);

        /* 如果是SETUP，那么就再解析client_port */
        if(!strcmp(method, "SETUP"))
        {
            while(1)
            {
                bufPtr = getLineFromBuf(bufPtr, line);
                if(!strncmp(line, "Transport:", strlen("Transport:")))
                {
                    sscanf(line, "Transport: RTP/AVP;unicast;client_port=%d-%d\r\n",
                                    &clientRtpPort, &clientRtcpPort);
                    break;
                }
            }
        }

        if(!strcmp(method, "OPTIONS"))
        {
            if(handleCmd_OPTIONS(sBuf, cseq))
            {
                printf("failed to handle options\n");
                goto out;
            }
        }
        else if(!strcmp(method, "DESCRIBE"))
        {
            if(handleCmd_DESCRIBE(sBuf, cseq, url))
            {
                printf("failed to handle describe\n");
                goto out;
            }
        }
        else if(!strcmp(method, "SETUP"))
        {
            if(handleCmd_SETUP(sBuf, cseq, clientRtpPort))
            {
                printf("failed to handle setup\n");
                goto out;
            }
        }
        else if(!strcmp(method, "PLAY"))
        {
            if(handleCmd_PLAY(sBuf, cseq))
            {
                printf("failed to handle play\n");
                goto out;
            }
        }
        else
        {
            goto out;
        }

        printf("---------------S->C--------------\n");
        printf("%s", sBuf);
        send(clientSockfd, sBuf, strlen(sBuf), 0);
    }
out:
    close(clientSockfd);
    free(rBuf);
    free(sBuf);
}

int main(int argc, char *argv[])
{
	int iRet = -1;
	RTSP_SERVER_T *ptRtspServer = NULL;
	ptRtspServer = app_get_rtsp_server_param();
	if(NULL == ptRtspServer)
	{
		printf("get param failed\n");
		return -1;
	}

	iRet = app_create_rtsp_socket();
	if(iRet < 0)
	{
		printf("app_create_rtsp_socket failed\n");
		return -1;
	}

	iRet = app_bind_sockaddr();
	if(iRet < 0)
	{
		printf("app_create_rtsp_socket failed\n");
		return -1;
	}
	iRet = listen(ptRtspServer->lfd_rtsp_socket,128);
	if(iRet < 0)
	{
		printf("listen failed\n");
		return -1;
	}

	ptRtspServer->lfd_rtp_socket = app_create_rtp_socket();
	ptRtspServer->lfd_rtcp_socket = app_create_rtcp_socket();
	if(ptRtspServer->lfd_rtp_socket < 0 ||
	   ptRtspServer->lfd_rtcp_socket < 0)
	{
		printf("app_create_rtp_socket or app_create_rtcp_socket failed \n");
		return -1;
	}

	while(1)
	{
		iRet = app_accept_client();
		if(iRet < 0)
		{
			printf("listen failed\n");
			return -1;
		}

		doClient(ptRtspServer->cfd_client, ptRtspServer->client_ip, ptRtspServer->client_port, ptRtspServer->lfd_rtp_socket, ptRtspServer->lfd_rtcp_socket);
	}
	
	
}

















